package com.ak.game.service;

import cn.hutool.core.bean.BeanUtil;
import com.ak.game.entity.BettingRecord;
import com.ak.game.entity.PnGame;
import com.ak.game.entity.vo.BetReqVo;
import com.ak.game.entity.vo.BetRspVo;
import com.ak.game.exception.ServiceException;
import com.ak.game.util.AjaxResult;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;

@Service
@Slf4j
public class BettingService {

    /**
     * 赔率
     */
    public static Map<String, BigDecimal> ODDS = new HashMap<>();

    /**
     * 杀率
     */
    public static Double WIN_RATE;

    /**
     * 预测结果随机数
     */
    public static BigDecimal predictionRandom;

    /**
     * 赔率初始化
     */
    @PostConstruct
    public void initOdds() {
        ODDS.clear();
        PnGame pnGame = gameService.getPnGame();
        String config = pnGame.getConfig();
        JSONObject jsonObject = JSONObject.parseObject(config);
        ODDS.put("number", jsonObject.getBigDecimal("number"));
        ODDS.put("red", jsonObject.getBigDecimal("red"));
        ODDS.put("red05", jsonObject.getBigDecimal("red05"));
        ODDS.put("green", jsonObject.getBigDecimal("green"));
        ODDS.put("green05", jsonObject.getBigDecimal("green05"));
        ODDS.put("purple", jsonObject.getBigDecimal("purple"));
        ODDS.put("oddOrEven", jsonObject.getBigDecimal("oddOrEven"));
        ODDS.put("bigOrSmall", jsonObject.getBigDecimal("bigOrSmall"));
    }

    /**
     * 杀率初始化
     */
    @PostConstruct
    public void initWinRate() {
        PnGame pnGame = gameService.getPnGame();
        WIN_RATE = pnGame.getWinRate();
    }

    /**
     * 注入下注记录类
     */
    @Autowired
    private BettingRecord bettingRecord;

    @Autowired
    private PnGameService gameService;

    @Qualifier("taskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor gameExecutor;

    /**
     * 异步接收下注请求
     *
     * @param betReqVo 下注的vo
     * @param ctx      通道代理上下文
     */
    @Async
    public void asyncPlaceBet1(BetReqVo betReqVo, ChannelHandlerContext ctx) {
        bettingRecord.placeBet1(betReqVo, ctx);
    }

    /**
     * 异步接收下注请求
     *
     * @param betReqVo 下注的vo
     * @param ctx      通道代理上下文
     */
    @Async
    public void asyncPlaceBet3(BetReqVo betReqVo, ChannelHandlerContext ctx) {
        bettingRecord.placeBet3(betReqVo, ctx);
    }

    /**
     * 异步接收下注请求
     *
     * @param betReqVo 下注的vo
     * @param ctx      通道代理上下文
     */
    @Async
    public void asyncPlaceBet5(BetReqVo betReqVo, ChannelHandlerContext ctx) {
        bettingRecord.placeBet5(betReqVo, ctx);
    }

    /**
     * 异步接收下注请求
     *
     * @param betReqVo 下注的vo
     * @param ctx      通道代理上下文
     */
    @Async
    public void asyncPlaceBet10(BetReqVo betReqVo, ChannelHandlerContext ctx) {
        bettingRecord.placeBet10(betReqVo, ctx);
    }

    /**
     * 刷新游戏配置
     */
    public void flushConfig() {
        // 获取新的游戏配置
        initOdds();
        // 返回到1分钟游戏场
        for (Map.Entry<String, ChannelHandlerContext> item : GameScheduler1.USER_CTX_MAP.entrySet()) {
            item.getValue().channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(ODDS, "1000"))));
        }
        // 返回到3分钟游戏场
        for (Map.Entry<String, ChannelHandlerContext> item : GameScheduler3.USER_CTX_MAP.entrySet()) {
            item.getValue().channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(ODDS, "1000"))));
        }
        // 返回到5分钟游戏场
        for (Map.Entry<String, ChannelHandlerContext> item : GameScheduler5.USER_CTX_MAP.entrySet()) {
            item.getValue().channel().writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(AjaxResult.success(ODDS, "1000"))));
        }
    }

    public void flushWinRate() {
        initWinRate();
    }

    /**
     * 异步计算下注结果
     *
     * @param betReqVo
     */
    @Async
    public CompletableFuture<BetRspVo> asyncGentleBet(BetReqVo betReqVo, Integer result) {
        return CompletableFuture.supplyAsync(() -> {
            BetRspVo betRspVo = new BetRspVo().setWin(BigDecimal.ZERO);
            BeanUtil.copyProperties(betReqVo, betRspVo);
            // 如果下注是0-9的数字，那么则直接和开奖的信息进行匹配
            if (betReqVo.getBetType() <= 9) {
                if (Objects.equals(betReqVo.getBetType(), result)) {
                    betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("number")));
                }
            } else { // 如果下注是颜色，则与颜色进行比对，10-红色 11-绿色 12-紫色
                switch (betReqVo.getBetType()) {
                    // 下注12则表示红色
                    case 12:
                        if (result.equals(2) || result.equals(4) || result.equals(6) || result.equals(8)) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("red")));
                        }
                        if (result.equals(0)) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("red05")));
                        }
                        break;
                    // 下注是11则表示绿色
                    case 11:
                        if (result.equals(1) || result.equals(3) || result.equals(7) || result.equals(9)) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("green")));
                        }
                        if (result.equals(5)) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("green05")));
                        }
                        break;
                    // 下注是10则表示紫色
                    case 10:
                        if (result.equals(0) || result.equals(5)) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("purple")));
                        }
                        break;
                    // 下注是13则表示单数
                    case 13:
                        if (result.equals(1) || result.equals(3) || result.equals(5) || result.equals(7) || result.equals(9)) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("oddOrEven")));
                        }
                        break;
                    // 下注是14则表示双数
                    case 14:
                        if (result.equals(0) || result.equals(2) || result.equals(4) || result.equals(6) || result.equals(8)) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("oddOrEven")));
                        }
                        break;
                    // 下注是15表示大
                    case 15:
                        if (result > 4) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("bigOrSmall")));
                        }
                        break;
                    // 下注是16则表示小
                    case 16:
                        if (result < 5) {
                            betRspVo.setWin(betReqVo.getAmount().multiply(ODDS.get("bigOrSmall")));
                        }
                        break;
                    default:
                        throw new ServiceException("10005");
                }
            }
            return betRspVo;
        }, gameExecutor);
    }

    /**
     * 发送AjaxResult类型的消息
     *
     * @param ajaxResult Ajax消息体
     * @param ctx        每次连接各自的通道代理上下文
     */
    public void returnMessageAjax(AjaxResult ajaxResult, ChannelHandlerContext ctx) {
        ctx.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(ajaxResult)));
    }

    /**
     * 发送字符串消息，可以是json字符串
     *
     * @param message 字符串消息体
     * @param ctx     每次连接各自的通道代理上下文
     */
    public void returnMessage(String message, ChannelHandlerContext ctx) {
        ctx.writeAndFlush(new TextWebSocketFrame(message));
    }
}
